<!--
 	* @author  dumeng
-->
<template>
  <ul class="imgList" :class="{ hideadd: crImages.length >= limit }">
    <li v-for="(item, i) in crImages" v-if="crImages.length > 0" :key="i">
      <span class="del" @click="delImg(i)"></span>
      <img :src="item && view + item" alt :onerror="defaultImg" />
    </li>
    <li v-if="crImages.length < limit">
      <div class="input-file">
        <input
          type="file"
          id="change"
          accept="image"
          multiple
          @change="img_upload($event)"
          name
          value
          class="img_input"
        />
      </div>
    </li>
  </ul>
</template>

<script>
import axios, { CancelToken } from 'axios'
import Qs from 'qs'
import defaultImg from '@/assets/images/base/error-icon.png'
export default {
  name: 'UploadList',
  data() {
    return {
      crImages: [],
      defaultImg: 'this.src="' + defaultImg + '"',
      view: '/API-DOCUMENT/document/file/download?id=',
      imgNum: 0,
      uploadFiles: [],
      fileIndex: 0,
      worker: null,
      fileData: []
    }
  },
  model: {
    prop: 'imgIds',
    event: 'change'
  },
  props: {
    // 选择的值
    imgIds: {
      type: String,
      default: ''
    },
    // 上传图片个数
    limit: {
      type: Number,
      default: 0
    },
    // 切片大小
    chunkSize: {
      type: Number,
      default: 10 * 1024 * 1024
    }
  },
  watch: {
    imgIds(val) {
      this.crImages = val ? val.split(',') : []
    }
  },
  methods: {
    img_click() { // 照片事件委托
      document.querySelector('.img_input').click()
    },
    img_upload(e) { // 照片上传
      var _this = this
      if (e.target.files.length <= 0) {
        return
      }
      for (var i = 0; i < e.target.files.length; i++) {
        if (e.target.files[i].type.indexOf('image') === -1) {
          _this.$dialog.toast({
            mes: '请上传图片类型的文件！',
            timeout: 1000
          })
          return false
        }
        _this.imgNum++
        if (_this.imgNum > _this.limit) {
          _this.$dialog.toast({
            mes: `最多上传${_this.limit}张照片！`,
            timeout: 1000
          })
          _this.imgNum = _this.crImages.length
          return false
        }
        this.uploadFiles = e.target.files
        this.$dialog.loading.open('正在上传,请稍后...')
        this.upload()
      }
    },
    // 上传图片
    async upload() {
      const that = this
      const tfile = this.uploadFiles[0]
      const chunknum = Math.ceil(tfile.size / this.chunkSize)
      const name = tfile.name || ''
      const size = tfile.size
      const md5 = await this.calculateHash(this.createFileChunk(tfile))
      const obj = {
        md5: md5,
        fileName: name,
        fileSize: size,
        chunkNum: chunknum,
        chunkSize: this.chunkSize
      }
      await this.getUUID(obj).then((data) => {
        const chunks = this.createFileChunk(tfile)
        tfile['UUID'] = data
        tfile['chunks'] = chunks.map(({ file }, index) => ({
          UUID: data,
          fileName: that.fileName,
          index,
          chunk: file,
          size: file.size
        }))
        this.uploadChunks(tfile)
      })
    },
    /**
     * 提交文件获取UUID
     */
    async getUUID(params) {
      return new Promise((resolve, reject) => {
        axios.post('/API-DOCUMENT/document/file/insert', params, {
          transformRequest: [(data) => {
            data = Qs.stringify(data)
            return data
          }]
        }).then((res) => {
          if (res.data.code === 200 || res.data.code === '200') {
            resolve(res.data.data)
          } else {
            this.$message.warning(res.data.msg || '文件读取失败')
            resolve()
          }
        }).catch((err) => {
          reject(err)
        })
      })
    },

    /**
     * 创建文件切片
     */
    createFileChunk(file, size = this.chunkSize) {
      const list = []
      var count = 0
      while (count < file.size) {
        list.push({
          file: file.slice(count, count + size)
        })
        count += size
      }
      return list
    },

    /**
     * 生成文件 hash（web-worker）
     */
    calculateHash(chunks) {
      return new Promise((resolve) => {
        this.worker = new Worker('./hash.js')
        this.worker.postMessage({ chunks })
        this.worker.onmessage = (e) => {
          const { hash } = e.data
          if (hash) {
            resolve(hash)
          }
        }
      })
    },

    /**
     * 将切片传输给服务端
     */
    async uploadChunks(file) {
      const _this = this
      var chunkData = file.chunks
      return new Promise(async (resolve, reject) => {
        const requestDataList = chunkData.map(({ chunk, UUID, fileName, index }) => {
          const formData = new FormData()
          formData.append('UUID', UUID)
          formData.append('file', chunk)
          formData.append('fileIndex', index) // 文件名使用切片的下标
          return { formData, index, fileName, UUID }
        })

        try {
          await this.sendRequest(requestDataList, chunkData)
        } catch (error) {
          return
        }

        try {
          await this.mergeRequest(file)
          resolve()
        } catch (error) {
          reject()
        }
      })
    },

    /**
     * 上传切片
     */
    sendRequest(forms, chunkData) {
      const that = this
      const {
        lang,
        field,
        ki
      } = this
      return new Promise((resolve, reject) => {
        const formInfo = forms.shift()
        const formData = formInfo.formData
        axios.post('/API-DOCUMENT/document/file/chunk', formData, {
          onUploadProgress: event => {
            if (event.lengthComputable) {
              that.progress = 100 * Math.round(event.loaded) / event.total
            }
          },
          headers: {
            'Content-Type': 'multipart/form-data'
          }
        }).then((res) => {
          if (res.data.code === 200 || res.data.code === '200') {
            resolve()
          } else {
            that.hasError = true
            that.errorMsg = lang.fail
            that.$emit('crop-upload-fail', res.data.msg, field, ki)
          }
        }).catch((e) => {
          if (that.value) {
            that.loading = 3
            that.hasError = true
            that.errorMsg = lang.fail
            that.$emit('crop-upload-fail', e, field, ki)
          }
        })
      })
    },

    /**
     * 通知服务端合并切片
     */
    mergeRequest(file) {
      const _this = this
      const {
        lang,
        field,
        ki
      } = this
      return new Promise((resolve, reject) => {
        axios.post('/API-DOCUMENT/document/file/merge', { UUID: file.UUID }, {
          transformRequest: [(data) => {
            data = Qs.stringify(data)
            return data
          }]
        }).then((res) => {
          if (res.data.code === 200 || res.data.code === '200') {
            _this.$dialog.loading.close()
            this.crImages.push(res.data.data.id)
            this.fileData.push(res.data.data)
            _this.$emit('change', this.crImages.join(','))
            _this.$emit('datas', this.fileData)
            resolve()
          } else {
            _this.$dialog.toast({
              mes: resolve.data.msg || '服务器繁忙请稍后再试。',
              timeout: 1000,
              icon: 'error'
            })
            resolve()
          }
        }).catch((e) => {
          _this.$dialog.toast({
            mes: '服务器繁忙请稍后再试。',
            timeout: 1000,
            icon: 'error'
          })
          reject()
        })
      })
    },
    delImg(i) {
      this.crImages.splice(i, 1)
      this.imgNum--
    }
  },
  created() {
    this.crImages = this.imgIds ? this.imgIds.split(',') : []
  },
  destroyed() {
    this.fileIndex = 0
  }

}
</script>

<style scoped lang="scss">
.imgList {
  margin-top: rem(20);
  display: flex;
  justify-content: flex-start;
  flex-wrap: wrap;
  &.hideadd li:last-child:after {
    display: none;
  }
  li {
    margin-right: rem(25);
    margin-bottom: rem(20);
    position: relative;
    background: #e5e5e5;
    border-radius: 5px;
    p {
      margin: 0;
      display: flex;
      justify-content: space-between;
      padding: 0 rem(10);
      font-size: rem(24);
      color: #949599;
    }
    .del {
      display: inline-block;
      position: absolute;
      top: rem(10);
      right: rem(10);
      width: rem(40);
      height: rem(40);
      background: url("../assets/images/base/delPhoto.png") no-repeat;
      background-size: rem(40) rem(40);
    }
    img {
      width: rem(150);
      height: rem(150);
      border-radius: 5px;
    }
    &:nth-child(4n) {
      margin-right: 0;
    }
    margin-bottom: rem(30);
    &:last-child {
      width: rem(150);
      height: rem(150);
      border: 1px dotted #e5e5e5;
      border-radius: 5px;
      background-color: #f0f0f0;
      position: relative;
      &:after {
        content: "";
        display: inline-block;
        width: rem(80);
        height: rem(80);
        /*border: 1px dotted #ddd;*/
        background: url("../assets/images/base/addPhoto.png") no-repeat;
        background-size: rem(80) rem(80);
        position: absolute;
        top: 50%;
        margin-top: rem(-40);
        left: 50%;
        margin-left: rem(-40);
      }
    }
    .input-file {
      width: 100%;
      height: 100%;
      position: absolute;
      opacity: 0;
      top: 50%;
      transform: translateY(-50%);
      z-index: 2;
      input {
        width: 100%;
        height: 100%;
      }
    }
  }
}
</style>
